;;;;;;;;;;;;;;;;;
; directory utils
;;;;;;;;;;;;;;;;;

;module
(env-push)

(defun files-all (&optional root exts cut_start cut_end)
	; (files-all [root exts cut_start cut_end]) -> paths
	;all source files from root downwards, none recursive
	(setd root "." exts '("") cut_start 0 cut_end -1)
	(defq stack (list root) paths (list))
	(while (setq root (pop stack))
		(unless (starts-with "./obj" root)
			(each! (lambda (file type)
				(cond
					((eql type "8")
						;file
						(if (some (# (ends-with %0 file)) exts)
							(push paths (cat root "/" file))))
					(:t ;dir
						(unless (starts-with "." file)
							(push stack (cat root "/" file))))))
				(unzip (split (pii-dirlist root) ",") 2))))
	(if (or (/= 0 cut_start) (/= -1 cut_end))
		(map (# (slice %0 cut_start cut_end)) paths)
		paths))

(defun files-dirs (paths)
	; (files-dirs paths) -> paths
	;return all the dir paths
	(reduce (lambda (dirs file)
		(defq dir (rfind "/" file) dir (if dir (cat (slice file 0 dir) ".")))
		(if (and dir (not (find dir dirs)))
			(push dirs dir) dirs)) paths (list)))

(defun files-depends (file)
	; (files-depends path) -> paths
	;create list of immediate dependencies
	(if (defq files (list) stream (file-stream file))
		(lines! (lambda (line)
			(and (eql "(" (first line))
				(or (starts-with "(include" line) (starts-with "(import" line))
				(push files (path-to-absolute
					(slice line (defq i (inc (find {"} line))) (find {"} line i)) file))))
			stream)) files)

(defun files-all-depends (files &optional imps)
	; (files-all-depends paths &optional imps) -> paths
	;create list of all dependencies, with implicit options
	(each-mergeable (# (task-slice) (each (lambda (&rest %0) (merge files %0)) (files-depends %0)))
		(setq files (merge (merge (usort (cat files)) (ifn imps '()))
			'("class/lisp/root.inc" "class/lisp/task.inc")))) files)

(defun files-scan (files handler &optional split_class comment)
	; (scan-files files handler [split_class comment]) -> files
	;iterates through files, processing lines.
	;new files returned by the handler are merged into the work list.
	(setd comment ";")
	(defq callback_e (penv))
	(unless (list? files) (setq files (list files)))
	(each-mergeable (lambda (file)
		(if (defq stream (file-stream file))
			(lines! (lambda (line) (task-slice)
				;skip empty lines and comments
				(unless (or (eql (defq linet (trim line)) +char_class_space)
							(and comment (starts-with comment linet)))
					;prepare input (tokens or trimmed line)
					(defq input (if split_class (split linet split_class) linet))
					;call user handler
					;merge any new dependencies returned by handler into files list
					(merge files (ifn (callback handler callback_e input file line (!)) '()))))
				stream))) files))

;module
(export-symbols '(files-all files-dirs files-all-depends files-depends files-scan))
(env-pop)
